<?php
/*!
 * \file
 * \brief YuToriクラスコアファイル
 * 
 * <pre>
 * YuToriクラスおよびYuToriObjectクラスを定義しているファイルです。
 * YuToriはこのファイルをインクルードするのみで使用できます。
 * 
 * おそらく大きな影響はないですが、最小化版としてyutori.min.phpも用意されています。
 * </pre>
 * \since ファイルの追加日
 */
 /**
  * @brief YuToriオブジェクト保持クラス
  * 
  * <pre>
  * このクラスは内部的なものであり、使用者がこれを意識することはおそらくないでしょう。
  * 
  * YuToriObjectクラスは、YuToriのスーパークラスであり、内部の動作を支えるものです。
  * YuToriのオブジェクトツリーを実際に保持するクラスで、実態としてはprotectedのプロパティを持っているだけのものです。
  * このクラスのインスタンスがオブジェクトツリーの実態であり、!YuToriはこのツリーの中から任意のものを抽出、選択して各種機能を提供しています。
  * </pre>
  * 
  * @note
  * 全てのプロパティがprotectedであるのは、YuToriがこのクラスを継承することで、!YuToriとは別のクラスのプロパティでありながら、!YuToriからこのクラスのインスタンスが持つプロパティに直接アクセスすることを可能とするためです。
  * 
  * @note
  * 一応、pickメソッドを使用することで、このクラスのインスタンスを取得することはできます。
  * 
  * @return void メソッドが存在しないため、何もありません。
  * 
  * @since 2012/01/31
  * @version 1.0
  * @author m.yuki
  * @date 2012/01/31 m.yuki 作成
  */
class YuToriObject {
	/**
	 * @brief 実際に保持する対象への参照
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	protected $_instance = null;
	
	/**
	 * @brief オブジェクトのツリー上での名称
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	protected $_name = '';
	
	/**
	 * @brief オブジェクトの型
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	protected $_type = YuTori::TYPE_EMPTY;
	
	/**
	 * @brief 子オブジェクトへの参照
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	protected $_children = array();
	
	/**
	 * @brief 親オブジェクトへの参照
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	protected $_parents = array();
	
	/**
	 * @brief YuToriObject各種情報
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	static protected $_info = array(
		'count_construct_object' => 0,
		'count_destruct_object' => 0,
		'count_object' => 0,
		'count_max_object' => 0,
	);
	
	/**
	 * @brief コンストラクタ
	 * 
	 * <pre>
	 * 各種情報を計算するだけで、特に動作はありません。
	 * </pre>
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	public function __construct () {
		YuToriObject::$_info['count_construct_object']++;
		if (++YuToriObject::$_info['count_object'] > YuToriObject::$_info['count_max_object']) YuToriObject::$_info['count_max_object'] = YuToriObject::$_info['count_object'];
	}
	
	/**
	 * @brief デストラクタ
	 * 
	 * <pre>
	 * 各種情報を計算するだけで、特に動作はありません。
	 * </pre>
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	public function __destruct () {
		YuToriObject::$_info['count_destruct_object']++;
		YuToriObject::$_info['count_object']--;
	}
}

/**
 * @brief YuTori動作の根幹をなすクラスです。
 * 
 * YuToriクラスはYuToriObjectが形成するオブジェクトツリーから任意のオブジェクトを取得して、様々な機能を提供するクラスです。
 * フレームワークの基礎となるクラスであり、このクラスファイル単独で動作することも可能です。
 * 
 * @author m.yuki shinokiwa@gmail.com
 */
class YuTori extends YuToriObject {
	static private $_root = null;
	static private $_selected = array();
	
	static protected $_info = array(
		'count_construct' => 0,
		'count_destruct' => 0,
		'count_YuTori' => 0,
		'count_max_YuTori' => 0,
	);
	
	protected $_object = null;
	protected $_selector = '';
	
	private $_methodResult = null;
	private $_callResult = null;
	private $_isCalled = false;
	private $_isOutput = true;
	
	
	const TYPE_NULL		=	0x01;
	const TYPE_BOOL		=	0x02;
	const TYPE_SCALAR		=	0x03;
	const TYPE_ARRAY		=	0x04;
	const TYPE_OBJECT		=	0x05;
	const TYPE_CALLABLE	=	0x06;
	const TYPE_AUTO		=	0x07;
	const TYPE_FILE		=	0x08;
	const TYPE_EMPTY		=	0xff;
	
	const STRING_THIS		=	'this';
	
	/**
	 * @brief コンストラクタです。
	 * 
	 * 基本的にはfactoryメソッドを使用して生成するものなので、newを使用してコンストラクタを動作させる事は少ないでしょう。
	 *
	 * @param YuToriObject $object セレクタとして指し示す対象のYuToriObject。
	 * @param mixed $selector 今回のYuToriを生成したセレクタ。指定しない場合空になります。
	 * @author m.yuki shinokiwa@gmail.com
	 * @date 2011/11/09 m.yuki 作成
	 * @date 2012/02/23 m.yuki $selectorに初期値を指定
	 * @date 2012/02/25 m.yuki $selectorの初期値をnullから空文字列に変更
	 */
	public function __construct (YuToriObject $instance, $selector = '') {
		$this->_selector = $selector;
		$this->_setObject ($instance);
		
		YuTori::$_info['count_construct']++;
		if (++YuTori::$_info['count_YuTori'] > YuTori::$_info['count_max_YuTori']) YuTori::$_info['count_max_YuTori'] = YuTori::$_info['count_YuTori'];
	}
	
	/**
	 * @brief デストラクタ
	 * 
	 * <pre>
	 * 各種情報を計算するだけで、特に動作はありません。
	 * </pre>
	 * 
	 * @access protected
	 * @since 2012/01/31
	 * @author m.yuki
	 * @date 2012/01/31 m.yuki 作成
	 */
	public function __destruct () {
		YuTori::$_info['count_destruct']++;
		YuTori::$_info['count_YuTori']--;
	}
	
	public static function init () {
		YuTori::$_root = null;
		YuTori::$_selected = array();
		
		YuTori::$_info = array(
			'count_construct' => 0,
			'count_destruct' => 0,
			'count_YuTori' => 0,
			'count_max_YuTori' => 0,
		);
		YuToriObject::$_info = array(
			'count_construct_object' => 0,
			'count_destruct_object' => 0,
			'count_object' => 0,
			'count_max_object' => 0,
		);
	}
	
	/**************** Methods for Instances ****************/
	
	/**
	 * factory
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#factory
	 */
	public static function factory ($instance, $name = null, array $children = array(), $type = YuTori::TYPE_AUTO) {
		try {
			$objYuTori = YuTori::newObject(&$instance, $name, &$children, $type);
			return new YuTori ($objYuTori, $objYuTori->_name);
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * newObject
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#factory
	 */
	public static function newObject ($instance, $name = null, array $children = array(), $type = YuTori::TYPE_AUTO) {
		try {
			$name = str_replace (array('.', ' '), '', $name);
			if (strcasecmp(YuTori::STRING_THIS, $name) === 0) throw new Exception ('YuToriObject cannot be named "this"');
			$newChildren = array();
			
			if ($instance instanceof YuToriObject) {
				if ($name) $instance->_name = $name;
				if (isset($instance->_object)) $objYuTori = $instance->_object;
				else $objYuTori = $instance;
				$newChildren = &$objYuTori->_children;
				unset ($objYuTori->_children);
				$objYuTori->_children = array();
			} else {
				$objYuTori = new YuToriObject ();
				$objYuTori->_name = $name;
				switch ($type) {
					case (YuTori::TYPE_CALLABLE) :
						if (is_callable($instance)) {
							$objYuTori->_type = YuTori::TYPE_CALLABLE;
							if ($name == null) {
								if (is_array($instance)) $objYuTori->_name = $instance[1];
								else $objYuTori->_name = $instance;
							}
						}
					break;
					case (YuTori::TYPE_FILE) :
						if (is_file($instance)) {
							$objYuTori->_type = YuTori::TYPE_FILE;
						}
					break;
					case (YuTori::TYPE_AUTO) :
						if (is_null($instance)) {
							$objYuTori->_type = YuTori::TYPE_NULL;
						} elseif (is_bool ($instance)) {
							$objYuTori->_type = YuTori::TYPE_BOOL;
						} elseif (is_scalar ($instance)) {
							$objYuTori->_type = YuTori::TYPE_SCALAR;
						} elseif (is_array ($instance)) {
							$objYuTori->_type = YuTori::TYPE_ARRAY;
							foreach ($instance as $key=>$val) {
								if (is_a ($val, 'YuToriObject')) {
									$newChildren[] = $val;
								} else {
									$child = YuTori::newObject (&$instance[$key], $key);
									$child->_parents[] = $objYuTori;
									$newChildren[] = $child;
								}
							}
							unset ($instance); $instance = array();
						} elseif (is_object ($instance)) {
							$objYuTori->_type = YuTori::TYPE_OBJECT;
						}
						break;
					default:
						break;
				}
				if ($objYuTori->_type == YuTori::TYPE_EMPTY) {
					$objYuTori->_name = null;
					$instance = null;
				}
				
				$objYuTori->_instance = &$instance;
			}
			foreach ($children as $key=>$val) {
				if (is_a ($val, 'YuToriObject')) {
					$newChildren[] = $val;
				} else {
					$child = YuTori::newObject (&$children[$key], $key);
					$child->_parents[] = $objYuTori;
					$newChildren[] = $child;
				}
			}
			$i = 0;
			foreach ($newChildren as $val) {
				if (is_numeric($val->_name)) $val->_name = $i++;
				$objYuTori->_children[] = $val;
			}
			
			return $objYuTori;
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**************** Methods for Selectors ****************/
	public function getSelector () {
		return $this->_selector;
	}
	
	static public function selected ($before = 0) {
		if ($cnt = count(YuTori::$_selected)) {
			return new YuTori (YuTori::$_selected[$cnt-1-$before]);
		} else {
			return YuTori::rootSelector ();
		}
	}
	
	/**
	 * rootSelector
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#rootSelector
	 */
	static public function rootSelector ($selector = '') {
		try {
			if (is_null(YuTori::$_root)) YuTori::$_root = YuTori::newObject(null, 'Root');
			if ($selector instanceof YuToriObject) {
				return new YuTori ($selector);
			} else {
				if (strlen ($selector) == 0) {
					return new YuTori (YuTori::$_root);
				} else{
					$object = new YuTori (YuTori::$_root);
					if (func_num_args() > 1) {
						$objRet = $object->_searchArray($object->_children, $selector, func_get_arg(1));
						if ($selector) return $objRet->child ($selector, func_get_arg(1));
						else return $objRet;
					} else {
						$objRet = $object->_searchArray($object->_children, $selector);
						if ($selector) return $objRet->child ($selector);
						else return $objRet;
					}
				}
			}
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**************** Internal Methods ****************/
	/**
	 * _setObject
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#_setObject
	 */
	private function _setObject (YuToriObject $object) {
		$this->_instance	= &$object->_instance;
		$this->_name		= &$object->_name;
		$this->_type		= &$object->_type;
		$this->_children	= &$object->_children;
		$this->_parents	= &$object->_parents;
		
		if (isset($object->_object)) $this->_object	= &$object->_object;
		else $this->_object	= &$object;
	}
	
	/**
	 * _openMethod
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#_openMethod
	 */
	private function _openMethod ($methodName, $callOption=0, &$arg1=null,&$arg2=null,&$arg3=null,&$arg4=null) {
		try {
			YuTori::$_selected[] = $this;
			$this->_methodResult = null;
			if ($callOption & 0x01) {
				$this->_callResult = null;
				$this->_isCalled = false;
			}
			if ($callOption & 0x10 && $this->_callResult == null) {
				$this->_callResult = $this->call();
			}
			return true;
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * _closeMethod
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#_closeMethod
	 */
	private function _closeMethod () {
		try {
			array_pop(YuTori::$_selected);
			return $this->_methodResult;
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**************** Methods for YuTori ****************/
	/**
	 * オブジェクトの型を返す
	 * ちなみに全部小文字。
	 * 
	 * @since 2011/10/18
	 */
	public function getType () {return $this->_type;}
	
	/**
	 * オブジェクトの名前を設定する
	 * string以外を指定すると何もしない。
	 * 
	 * @since 2011/10/18
	 * @return string オブジェクトの名称
	 */
	public function setName ($name) {
		if (is_scalar ($name)) $this->_name = $name;
		return $this->_name;
	}
	
	/**
	 * オブジェクトの名前を返す
	 * 
	 * @since 2011/10/18
	 * @return string オブジェクトの名称
	 */
	public function getName () {
		return $this->_name;
	}
	
	/**
	 * Rootから見たオブジェクトの絶対パスを返す
	 * 
	 * @since 2011/10/18
	 * @return string オブジェクトの名称
	 */
	public function getFullName () {
		$fullName = $this->_name;
		$obj = $this;
		while (isset ($obj->_parents[0])) {
			$obj = $obj->_parents[0];
			$fullName = $obj->_name. '.'. $fullName;
		}
		return $fullName;
	}
	
	/**************** Methods for Manipulate ****************/
	
	/**
	 * 保持インスタンスを取得する。
	 * array型の場合、配下のオブジェクトを全て配列として返す。
	 * (child()指定時と同様)
	 * 
	 * @since 2011/11/09
	 */
	public function get () {
		try {
			if ($this->_openMethod (__FUNCTION__)) {
				switch ($this->_type) {
					case YuTori::TYPE_ARRAY :
						$this->_methodResult = array();
					$index = -1;
					while (isset($this->_children[++$index])) {
						$obj = new YuTori ($this->_children[$index], $this->_selector.'.'.$this->_children[$index]->_name);
						$this->_methodResult[$obj->_name] = $obj->get();
					}
					break;
					case YuTori::TYPE_FILE :
						$this->_methodResult = file_get_contents($this->_instance);
					break;
					default: 
						$this->_methodResult = $this->_instance;
					break;
				}
			}
			return $this->_closeMethod ();
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * set
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#set
	 */
	public function set ($instance, $type = YuTori::TYPE_AUTO) {
		try {
			if ($this->_openMethod (__FUNCTION__, 0x01)) {
				$this->_methodResult = $this;
				$objYuTori = YuTori::newObject(&$instance, $this->_name, array(), $type);
				if ($objYuTori->_type !== YuTori::TYPE_EMPTY) {
					$this->_object->_children = array_merge($this->_children, $objYuTori->_children);
					$this->_object->_instance = $objYuTori->_instance;
					$this->_object->_type = $objYuTori->_type;
				}
			} return $this->_closeMethod ();
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * インスタンスを保持しているYuToriObjectインスタンスを返す。
	 * 
	 * @since 2012/01/21
	 */
	public function pick () { return $this->_object; }
	
	/**
	 * 内包インスタンスをcallる。
	 * callの効果は型によって異なる。
	 * null、scalarはそのままの値を返す。
	 * arrayは全ての子オブジェクトに対してcallをかける。
	 * objectは内包するオブジェクトにcallという名のメソッドがあればそれを呼び、なければオブジェクト自体を返す。
	 * functionは内包する関数を実行する。
	 * 
	 * なお、callは性質上参照引数を持てない上、他のメソッドがcallをかける場合ほとんど引数を取らないため、
	 * 可能であれば必要な値は全てオブジェクトツリーに登録する事を推奨する。
	 * (強い推奨ではないため、引数を持つこと自体は違反ではない)
	 * 
	 * @since 2011/11/20
	 */	
	public function call () {
		try {
			if ($this->_openMethod (__FUNCTION__, 0)) {
				switch ($this->_type) {
					case YuTori::TYPE_ARRAY :
						$this->_methodResult = array();
					$index = -1;
					while (isset($this->_children[++$index])) {
						$obj = new YuTori ($this->_children[$index], $this->_selector.'.'.$this->_children[$index]->_name);
						$this->_methodResult[$obj->_name] = call_user_func_array(array($obj, "call"), func_get_args());
					}
					break;
					case YuTori::TYPE_OBJECT :
						if (method_exists ($this->_instance, "call")) {
							$this->_methodResult = call_user_func_array(array($this->_instance, "call"), func_get_args ());
						} else {
							$this->_methodResult = $this->_instance;
						}
					break;
					case YuTori::TYPE_CALLABLE :
						$this->_methodResult = call_user_func_array($this->_instance, func_get_args ());
					break;
					case YuTori::TYPE_FILE :
						include ($this->_instance);
					break;
					case YuTori::TYPE_EMPTY :
						$this->_methodResult = null;
					break;
					default: 
						$this->_methodResult = $this->_instance;
					break;
				}
			}
			$this->_callResult = $this->_methodResult;
			$this->_isCalled = true;
			return $this->_closeMethod ();
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**************** Methods for Families ****************/
	
	/**
	 * add
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#add
	 */
	public function add ($instance, $name = null, $children = array(), $type = YuTori::TYPE_AUTO) {
		try {
			if ($this->_openMethod (__FUNCTION__, 0, $instance, $name, $children, $type)) {
				$child = YuTori::factory (&$instance, $name, $children, $type);
				if ($child->_type != YuTori::TYPE_EMPTY) {
					$child->_parents[] = $this->_object;
					if ($child->_name) {
						foreach ($this->_children as $key=>$obj) {
							if (strcasecmp($child->_name, $obj->_name) === 0) {
								unset ($this->_children[$key]);
								$this->_children = array_values ($this->_children);
								foreach ($obj->_parents as $parentKey=>$parentObj) {
									if (strcasecmp($this->_name, $parentObj->_name) === 0) {
										unset ($parentObj->_parents[$parentKey]);
										$parentObj->_parents = array_values ($parentObj->_parents);
									}
								}
							}
						}
					} else {
						$child->_name = count ($this->_children);
					}
					$this->_children[] = $child;
				}
				$this->_methodResult = new YuTori ($child, $this->_selector.'.'.$child->_name);
			}
			return $this->_closeMethod (__FUNCTION__);
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * セレクタを使って子オブジェクトを取得する
	 * 定義上は存在しないように見えるが、第二引数に値を指定すると、
	 * 子オブジェクトが見つからなかった時に返す値を指定できる。
	 * 
	 * @since 2011/10/18
	 * @param mixed $Selector セレクタ。
	 * @param mixed セレクタ指定で何も見つからなかった時に返す値。
	 * @return YuTori 取得した子オブジェクト。子が存在しなかった場合、empty型を返す(第二引数がない場合)。
	 */
	public function child ($selector = '') {
		try {
			if ($this->_openMethod (__FUNCTION__, 0, $selector)) {
				if (func_num_args() > 1) {
					$objRet = $this->_searchArray($this->_children, $selector, func_get_arg(1));
					if ($selector) $this->_methodResult = $objRet->child ($selector, func_get_arg(1));
					else $this->_methodResult = $objRet;
				} else {
					$objRet = $this->_searchArray($this->_children, $selector);
					if ($selector) $this->_methodResult = $objRet->child ($selector);
					else $this->_methodResult = $objRet;
				}
			}
			return $this->_closeMethod();
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * 親オブジェクトを取得する
	 * 引数はchildと同様。
	 * 
	 * @since 2011/10/18
	 */
	public function parent ($selector = '') {
		try {
			if ($this->_openMethod (__FUNCTION__, 0, $selector)) {
				if (func_num_args() > 1) {
					$objRet = $this->_searchArray($this->_parents, $selector, func_get_arg(1));
					if ($selector) $this->_methodResult = $objRet->parent ($selector, func_get_arg(1));
					else $this->_methodResult = $objRet;
				} else {
					$objRet = $this->_searchArray($this->_parents, $selector);
					if ($selector) $this->_methodResult = $objRet->parent ($selector);
					else $this->_methodResult = $objRet;
				}
			}
			return $this->_closeMethod();
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * 同じ親を持つ同階層のオブジェクトを取得する。
	 * parent、childを連続して実行するだけ。
	 * ただしセレクタ引数順は子・親なので注意。
	 * 第三引数に失敗時の値を入れられる。
	 * 
	 * @since 2011/10/18
	 */
	public function neighbor ($childSelector = '', $parentSelector = 0) {
		try {
			if (($objParent = $this->parent($parentSelector, false)) && ($objChild = $objParent->child($childSelector))) {
				return $objChild;
			}
			if (func_num_args() > 2) return func_get_arg(2);
			else return new YuTori (YuTori::newObject('', null, YuTori::TYPE_EMPTY), $childSelector);
		} catch (Exception $e) {
			throw new Exception ($e->getMessage(), $e->getCode(), $e);
		}
	}
	
	/**
	 * child、parentの比較用サブメソッド
	 * 
	 * @since 2011/11/20
	 */
	private function _searchArray (&$array, &$orgSelector) {
		$selector = $orgSelector;
		$orgSelector = null;
		if ($selector === '') {
			// セレクタが空の時はオブジェクトを全て返す。
			$arrObject = YuTori::factory($array);
			return $arrObject;
		} elseif (is_object ($selector)) {
			foreach ($array as $key=>$obj) {
				if (($selector instanceof YuToriObject && $obj->_instance === $selector->_instance) || ($selector === $obj->_instance)) {
					return new YuTori ($obj, $selector);
				}
			}
		} elseif (is_scalar($selector)) {
			// 現在のセレクタを判定
			$selector = str_replace (' ', '', $selector);
			$exSelector = explode ('.', $selector, 2);
			if (isset($exSelector[1])) $orgSelector = trim($exSelector[1], '.');
			
			// セレクタが数値か文字列か
			if (is_numeric ($exSelector[0])) {
				if (isset ($array[$exSelector[0]])) return new YuTori ($array[$exSelector[0]], $this->_selector.'.'.$exSelector[0]);
			} elseif (is_string ($exSelector[0]) && strcasecmp($exSelector[0], YuTori::STRING_THIS)===0) {
				return YuTori::selected();
			} elseif (is_string ($exSelector[0])) {
				foreach ($array as $key=>$obj) {
					if (strcasecmp ($exSelector[0], $obj->_name) == 0) return new YuTori ($array[$key], $this->_selector.'.'.$exSelector[0]);
				}
			}
		}
		if (func_num_args() > 2) return func_get_arg(2);
		else return new YuTori (YuTori::newObject('', null, array(), YuTori::TYPE_EMPTY), $selector);
	}
	
	/**
	 * 子オブジェクトの数を返す
	 * 
	 * @since 2012/01/17
	 */
	public function count () {
		return count ($this->_children);
	}
	
	/**
	 * 子オブジェクトを削除する
	 * 
	 * @since 2011/10/18
	 * @param string $name 子オブジェクトの名称
	 * @return YuTori 自分自身。削除に成功しようと失敗しようと返す。
	 */
	public function remove ($Name) {
		// 子を検索
		foreach ($this->_children as $key=>$Child) {
			if (strcasecmp($Child->_name,$Name) === 0) {
				foreach ($Child->_parents as $unsetKey=>$unsetObj) {
					if ($Child === $unsetObj) {
						unset ($Child->_parents[$unsetKey]);
						$Child->_parents = array_values ($Child->_parents);
					}
				}
				unset($this->_children[$key]);
				$this->_children = array_values ($this->_children);
			}
		}
		return $this;
	} 

	/**************** Methods of Filter ****************/
	/**
	 * @brief クラスフィルタを登録します。
	 * 
	 * クラスフィルタはインスタンス化済み、これからインスタンス化にかかわらず
	 * すべてのYuToriに対して影響を与えます。
	 * なお、同一のクラスを２つ以上登録しようとした場合、登録されません。
	 *
	 * @subpage filter
	 * @test TestCaseofFilter
	 * @param Mixed フィルタとなるクラスのインスタンス、もしくはクラス名を指定します。配列で複数登録することもできます。
	 * @return void 戻り値はありません。
	 * @author m.yuki shinokiwa@gmail.com
	 * @date 2012/05/20 m.yuki 作成
	 */
	public static function regClassFilter ($class) {
		
	}

	/**
	 * @brief クラスフィルタを解除します。
	 * 
	 * 登録されたクラスフィルタを解除します。
	 * フィルタとして登録していないクラスを指定した場合、何もしません。
	 *
	 * @subpage filter
	 * @test TestCaseofFilter
	 * @param Mixed 解除するフィルタのインスタンス、もしくはクラス名を指定します。配列で複数指定することもできます。
	 * @author m.yuki shinokiwa@gmail.com
	 * @date 2012/05/20 m.yuki 作成
	 */
	public static function unregClassFilter ($class) {
		
	}

	/**
	 * @brief オブジェクトフィルタを登録します。
	 * 
	 * オブジェクトフィルタは特定のYuToriObjectに対してのみ動作し、影響を及ぼします。
	 * なお、同一のクラスを２つ以上登録しようとした場合、登録されません。
	 *
	 * @subpage filter
	 * @test TestCaseofFilter
	 * @param Mixed フィルタとなるクラスのインスタンス、もしくはクラス名を指定します。配列で複数登録することもできます。
	 * @return void 戻り値はありません。
	 * @author m.yuki shinokiwa@gmail.com
	 * @date 2012/05/20 m.yuki 作成
	 */
	public static function regObjectFilter ($class) {
		
	}
	
	/**
	 * @brief オブジェクトフィルタを解除します。
	 * 
	 * 登録されたオブジェクトフィルタを解除します。
	 * フィルタとして登録していないクラスを指定した場合、何もしません。
	 *
	 * @subpage filter
	 * @test TestCaseofFilter
	 * @param Mixed 解除するフィルタのインスタンス、もしくはクラス名を指定します。配列で複数指定することもできます。
	 * @author m.yuki shinokiwa@gmail.com
	 * @date 2012/05/20 m.yuki 作成
	 */
	public static function unregObjectClassFilter ($class) {
		
	}
	
	/**************** 比較系メソッド ****************/
	/* call結果の比較はsetDefaultやsetFormat等の後に使用するとその内容で判定するので注意。*/
	
	/**
	 * 自身をcallした結果と等価比較を行う。
	 * 型までは見ないので、==と同等。
	 * 
	 * callした結果を比較するため、callされると都合が悪い場合は使用しないこと。
	 * また、このcallには引数指定ができない。
	 * 
	 * @since 2012/01/22
	 * @param mixed $Value 比較相手の値。YuToriおよびYuToriObjectをいれている場合、call結果と比較する。
	 * @return bool 等価比較を行った結果。
	 */
	public function eq ($Value) {
		if (is_object($Value)) {
			if ($Value instanceof YuToriObject) {
				$Value = YuTori::factory('', $Value)->call();
			}
		}
		if (!$this->_outputFlg) $this->call();
		return $this->_output == $Value;
	}
	
	/**
	 * 自身をcallした結果と厳密等価比較を行う。
	 * ===と同等。
	 * 
	 * callした結果を比較するため、callされると都合が悪い場合は使用しないこと。
	 * また、このcallには引数指定ができない。
	 * 
	 * @since 2012/01/22
	 * @param mixed $Value 比較相手の値。YuToriおよびYuToriObjectをいれている場合、call結果と比較する。
	 * @return bool 等価比較を行った結果。
	 */
	public function seq ($Value) {
		if (is_object($Value)) {
			if ($Value instanceof YuToriObject) {
				$Value = YuTori::factory('', $Value)->call();
			}
		}
		if (!$this->_outputFlg) $this->call();
		return $this->_output === $Value;
	}
	
	
	/**
	 * 空オブジェクトかどうか返す
	 * 
	 * @since 2012/01/21
	 * @return bool 空オブジェクト(empty型)の場合true。ほかはfalse。
	 */
	public function isEmpty () {
		return $this->_type == YuTori::TYPE_EMPTY;
	}
	
	/**
	 * call結果が空かどうか返す
	 * null、0、false、空文字列の場合trueになる。
	 * 
	 * @since 2012/02/14
	 */
	public function isNull () {
		if (!$this->_outputFlg) $this->call();
		return !$this->_output;
	}
	
	/**
	 * call結果が文字列かどうか返す
	 * 
	 * @since 2012/02/14
	 */
	public function isString () {
		if (!$this->_outputFlg) $this->call();
		return is_string($this->_output);
	}
	
	/**
	 * call結果が数値かどうか返す
	 * 
	 * @since 2012/02/14
	 */
	public function isNumeric () {
		if (!$this->_outputFlg) $this->call();
		return is_numeric($this->_output);
	}
	
	/**
	 * call結果が配列かどうか返す
	 * 
	 * @since 2012/02/14
	 */
	public function isArray () {
		if (!$this->_outputFlg) $this->call();
		return is_array($this->_output);
	}
	
	/**
	 * call結果がオブジェクトかどうか返す
	 * 
	 * @since 2012/02/14
	 */
	public function isObject () {
		if (!$this->_outputFlg) $this->call();
		return is_object($this->_output);
	}
	
	/**************** 出力系メソッド ****************/
	
	/**
	 * _outputをリセットする。
	 * 
	 * @since 2012/01/30
	 * @return YuTori 自分自身
	 */
	public function reset () {
		$this->_outputFlg = false;
		$this->_outputEmpty = false;
		return $this;
	}
	
	/**
	 * _outputをフォーマットする。
	 * sprintfを使用するので、同関数に使用できるフォーマットは全て使用可能。
	 * 
	 * @since 2012/01/30
	 * @param string $format sprintfに渡すフォーマット指定。
	 * @return YuTori 自分自身
	 */
	public function format ($format = '%s') {
		if ($this->_openMethod(__FUNCTION__, 0x10, func_get_args())) {
			$this->_methodResult = $this;
			if ($this->_isOutput) {
				$this->_callResult = sprintf ($format, $this->_callResult);
			}
		} return $this->_closeMethod();
	}
	
	/**
	 * _outputが空の際に出力するものを指定する。
	 * 空とは、null、false、もしくは0バイトの文字列、数値の0になる。
	 * 
	 * @since 2012/01/30
	 * @param string $default _outputが空の際に出力する文字列
	 * @return YuTori 自分自身
	 */
	public function noResult ($default = null) {
		if ($this->_openMethod(__FUNCTION__, 0x10, func_get_args())) {
			if ($this->_isOutput && !$this->_callResult) {
				$this->_callResult = $default;
			}
		} return $this->_closeMethod();
	}
	
	/**
	 * 文字列のエスケープ処理を行う
	 * 現在対応しているエスケープ処理は以下の通り。
	 * HTML
	 * 
	 * @since 2012/02/08
	 * @param string $type エスケープ処理を行うタイプ
	 */
	public function escape ($type) {
		if ($this->_outputEmpty) return $this;
		if (!$this->_outputFlg) $this->call();
		switch (strtolower ($type)) {
			case ('html'):
				$this->_output = htmlspecialchars ($this->_output);
			break;
			default:
				break;
		}
		return $this;
	}
	
	/**
	 * put
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#put
	 */
	public function put () {
		print $this->sput();
		return $this;
	}
	
	/**
	 * sput
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#sput
	 */
	public function sput () {
		if ($this->_openMethod(__FUNCTION__, 0x10, func_get_args())) {
			$formats = func_get_args ();
			foreach ($formats as $strFormat) {
				$format = explode ('=', $strFormat, 2);
				if (method_exists($this, $format[0])) {
					if (count($format) == 2) $this->$format[0]($format[1]);
					else $this->$format[0]();
				} elseif(is_callable($format[0])) {
					if (count($format) == 2) $this->_callResult = $format[0]($this->_callResult, $this->$format[1]);
					else $this->_callResult = $format[0]($this->_callResult);
				}
			}
			$this->_methodResult = $this->_callResult;
		}
		return $this->_closeMethod();
	}
	/**
	 * YuToriObjectにダンプをかける。
	 * var_dumpすると無限ループするため専用メソッド作成。
	 * parentsは出力されない。
	 * 
	 * @since 2012/01/21
	 * @param int $indent インデントの数。再帰処理に使うので基本的に指定なしで。
	 * @return bool 空オブジェクト(empty型)の場合true。ほかはfalse。
	 */
	public function dump () {
		print ('"'.$this->_selector. '"');
		print (' [name:"'.$this->_name.'"]');
		print (' [type:"'.$this->_type.'"]');
		print ('{'."\n");
		var_dump ($this->_instance);
		print ('}'."\n");
		foreach ($this->_children as $Key=>$Val) {
			$this->child ($Val->_name)->dump ();
		}
		print("\n");
		return $this->_type == YuTori::TYPE_EMPTY;
	}
	
	/**************** Object型用メソッド ****************/
	
	/**
	 * オブジェクトのプロパティを参照するか、変更する
	 * 引数なしの場合、全てのプロパティをArray型YuToriで取得
	 * 引数がひとつの場合、指定したプロパティの値を取得
	 * 引数がふたつの場合、指定したプロパティに第二引数の値を代入
	 * 
	 * @since 2012/02/11
	 * @param string $name プロパティ名
	 * @param mixed $value 代入する値
	 * @return mixed 上記参照
	 */
	function prop () {
		if ($this->_type != YuTori::TYPE_OBJECT) return $this;
		$cntArgs = func_num_args();
		if ($cntArgs == 0) {
			return YuTori::factory('', get_object_vars($this->_instance));
		} elseif ($cntArgs == 1) {
			$property = func_get_arg(0);
			if (property_exists($this->_instance, $property)) {
				return $this->_instance->$property;
			} else {
				return null;
			}
		} elseif ($cntArgs > 1) {
			$property = func_get_arg(0);
			$value = func_get_arg(1);
			$this->_instance->$property = $value;
			return $this;
		}
	}
	
	/**
	 * オブジェクト内のメソッドを一時的にCallable型YuTori化して取得する
	 * メソッドチェーンでcall可能。
	 * 
	 * @since 2012/02/11
	 * @param string $method YuTori化するメソッド
	 */
	function method ($method) {
		if ($this->_type != YuTori::TYPE_OBJECT) return $this;
		if (method_exists($this->_instance, $method)) {
			return Y($method, array($this->_instance, $method), YuTori::TYPE_CALLABLE);;
		} else {
			return Y($method, null, YuTori::TYPE_EMPTY);;
		}
	}
	
	/**
	 * オブジェクト内のプロパティを全て子オブジェクトとして登録する
	 * ちなみに値は参照渡しで登録される。
	 * 
	 * @since 2012/02/11
	 */
	function addAllProp () {
		if ($this->_type != YuTori::TYPE_OBJECT) return $this;
		foreach (get_object_vars($this->_instance) as $key=>$var) {
			$this->add ($key, &$this->_instance->$key);
		}
		return $this;
	}
	
	/**
	 * オブジェクト内のメソッドを全て子オブジェクトとして登録する
	 * beforeGetやcall等、YuTori用のメソッドは除外される。
	 * 
	 * @since 2012/02/11
	 */
	function addAllMethod () {
		// 登録しないメソッド
		$nonAdd = array('call', 'beforeGet', 'afterGet', 'beforeSet', 'afterSet');
		foreach (get_class_methods ($this->_instance) as $var) {
			if (array_search ($var, $nonAdd) === false) {
				$this->add ($var, array($this->_instance, $var), YuTori::TYPE_CALLABLE);
			}
		}
		return $this;
	}
	
	/**
	 * オブジェクト内の全メンバを子オブジェクトとして登録する
	 * プロパティとメソッドで名称が重複している場合、メソッドが優先される。
	 * 
	 * @since 2012/02/11
	 */
	function addAllMember () {
		$this->addAllAttr();
		$this->addAllMethod();
		return $this;
	}
	
	/**
	 * info
	 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#info
	 */
	public static function info () {
		$ret = array_merge (YuTori::$_info, YuToriObject::$_info);
		return $ret;
	}
}

/**
 * Y
 * @link https://code.google.com/p/yutori-php-framework/wiki/YuTori#Y
 */
function Y ($Selector = '') {
	if (func_num_args() > 1) return YuTori::rootSelector($Selector, func_get_arg(1));
	else return YuTori::rootSelector($Selector);
}

/**
 * ショートコード設定用クラスY
 * 
 * 単に関数名重複を避けるためにクラス化してあるだけなので、全てstaticメソッド。
 * ※YuToriクラスの定義内では使用しないこと！
 * 
 * @since 2012/01/31
 */
class Y {
	/**
	 * YuTori::newObject('', $args, YuTori::TYPE_CALLABLE);のショートコード。
	 * 無名で生成するが、addやsetに使用する際に命名される。
	 * 
	 * @since 2012/01/31
	 */
	static function func () {
		$args = func_get_args (); if (count($args) == 1) $args = $args[0];
		return YuTori::factory($args, '', array(), YuTori::TYPE_CALLABLE);
	}
	
	/**
	 * YuTori::newObject('', $args, YuTori::TYPE_FILE);のショートコード。
	 * 無名で生成するが、addやsetに使用する際に命名される。
	 * 
	 * @since 2012/01/31
	 */
	static function file ($file) {
		return YuTori::factory($file, '', array(), YuTori::TYPE_FILE);
	}
	
	/**
	 * Y($arg)->put();のショートコード。
	 * putの引数は使えないので注意。
	 * 
	 * @since 2012/01/31
	 */
	static function put ($selector) {
		return Y($selector)->put();
	}
}
